import { BaseRoleId } from '@/config/constant';
import { NoLogin } from '@/decorator/no-login.decorator';
import { SessionGuard } from '@/guard/session.guard';
import { ResponseBody, ResponseCode } from '@/utils/ResponseCode';
import {
  Body,
  Controller,
  Post,
  Session,
  Put,
  UseGuards,
  Get,
  HttpException,
  HttpStatus,
  Param,
  Query,
  Req,
  Delete,
} from '@nestjs/common';
import { EmailService } from '../email/email.service';
import {
  CreateUserRequest,
  GetUsersRequest,
  UserLoginRequest,
  UserModifyPasswordRequest,
  UserModifyRequest,
  UserRegisterRequest,
} from './dto/request.dto';
import { LoginResponse } from './dto/response.dto';
import { UserService } from './user.service';
import { User as UserEntity } from '@/entity/User';
import { Permission as PermissionEntity } from '@/entity/Permission';
import { createPermissionTreeByPath } from '@/utils/createPermissionTreeByPath';
import randomCode from '@/utils/randomCode';
import { Request as HttpRequest } from 'express';
import { SessionType } from '@/types';

@UseGuards(SessionGuard)
@Controller('user')
export class UserController {
  constructor(
    private readonly userService: UserService,
    private readonly emailService: EmailService,
  ) {}

  // 获取用户列表
  @Get()
  async getUsers(
    @Query() query: GetUsersRequest,
    @Req() request: HttpRequest,
    @Session() session: SessionType,
  ) {
    // 接口鉴权
    await this.userService.verifyPermission(request, session.username);
    const { current, pageSize, ...findWhere } = query;
    const { total, list } = await this.userService.findAllAndCount(findWhere, {
      current,
      pageSize,
    });
    return new ResponseBody<{
      total: number;
      list: UserEntity[];
    }>({
      data: {
        total,
        list,
      },
      message: '获取用户列表成功',
    });
  }

  // 注册
  @NoLogin()
  @Post('register')
  async register(
    @Body() requestBody: UserRegisterRequest,
    @Session() session: SessionType,
  ) {
    if (!(session && session.userCode === requestBody.code)) {
      return new ResponseBody<LoginResponse>({
        code: ResponseCode.Fail,
        message: '验证码错误',
      });
    }
    const { email, username, password } = requestBody;
    const [existUser, existEmail] = await Promise.all([
      this.userService.findOneBy({ username }),
      this.userService.findOneBy({ email }),
    ]);
    if (existUser) {
      return new ResponseBody<LoginResponse>({
        code: ResponseCode.Fail,
        message: '用户名已存在',
      });
    }
    if (existEmail) {
      return new ResponseBody<LoginResponse>({
        code: ResponseCode.Fail,
        message: '邮箱已经存在',
      });
    }
    try {
      // 创建用户，并赋予 基础的角色
      const userEntity = await this.userService.createUser([BaseRoleId], {
        email,
        username,
        password,
      });
      // 注册后直接登入
      session.username = userEntity.username;
      session.userId = userEntity.id;
      // 返回注册用户id
      return new ResponseBody<number>({
        message: '注册成功',
        data: userEntity.id,
      });
    } catch (error) {
      // 事务抛出错误的兜底
      throw new HttpException(
        {
          message: error,
          code: ResponseCode.Fail,
          data: null,
        },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  // 管理系统的创建用户
  @Post()
  async createUser(
    @Body() requestBody: CreateUserRequest,
    @Req() request: HttpRequest,
    @Session() session: SessionType,
  ) {
    // 接口鉴权
    await this.userService.verifyPermission(request, session.username);
    const { roleIds, ...userInfo } = requestBody;
    const [existUser, existEmail] = await Promise.all([
      this.userService.findOneBy({ username: userInfo.username }),
      this.userService.findOneBy({ email: userInfo.email }),
    ]);
    if (existUser) {
      return new ResponseBody<LoginResponse>({
        code: ResponseCode.Fail,
        message: '用户名已存在',
      });
    }
    if (existEmail) {
      return new ResponseBody<LoginResponse>({
        code: ResponseCode.Fail,
        message: '邮箱已经存在',
      });
    }
    try {
      // 创建用户，并赋予 基础的角色
      const userEntity = await this.userService.createUser(roleIds, userInfo);
      // 返回创建的用户id
      return new ResponseBody<number>({
        message: '创建用户成功',
        data: userEntity.id,
      });
    } catch (error) {
      // 事务抛出错误的兜底
      throw new HttpException(
        {
          message: error,
          code: ResponseCode.Fail,
          data: null,
        },
        HttpStatus.INTERNAL_SERVER_ERROR,
      );
    }
  }

  @Put('base_info')
  async updateUserBySession(
    @Req() request: HttpRequest,
    @Session() session: SessionType,
    @Body() requestBody: UserModifyRequest,
  ) {
    // 接口鉴权
    await this.userService.verifyPermission(request, session.username);
    const { ...toUpdateUserInfo } = requestBody;
    await this.userService.update(
      {
        id: session.userId,
      },
      toUpdateUserInfo,
    );
    return new ResponseBody('更新成功');
  }

  // 登入 => 获取身份 => 获取权限
  @NoLogin()
  @Post('login')
  async login(
    @Body() requestBody: UserLoginRequest,
    @Session() session: SessionType,
  ) {
    const { username, password } = requestBody;
    const userEntityData = await this.userService.findOneBy({ username });
    // 没有找到该用户
    if (!userEntityData) {
      return new ResponseBody({
        message: '用户不存在',
        code: ResponseCode.Fail,
      });
    }
    // 密码错误
    if (userEntityData.password !== password) {
      return new ResponseBody({
        message: '密码错误',
        code: ResponseCode.Fail,
      });
    }
    // 登入成功，但会所有的角色 ids
    session.username = userEntityData.username;
    session.userId = userEntityData.id;
    return new ResponseBody({
      message: '登入成功',
      data: userEntityData.id,
      code: ResponseCode.Success,
    });
  }

  // 修改密码
  @NoLogin()
  @Put('password')
  async modifyUserPassword(
    @Body() requestBody: UserModifyPasswordRequest,
    @Req() request: HttpRequest,
    @Session() session: SessionType,
  ) {
    const { code, password, email, oldPassword } = requestBody;
    const userEntityData = await this.userService.findOneBy({ email });
    if (!userEntityData) {
      return new ResponseBody({
        message: '用户不存在',
        code: ResponseCode.Fail,
      });
    }
    // 验证码模式
    if (code && session.userCode !== code) {
      return new ResponseBody({
        message: '验证码错误',
        code: ResponseCode.Fail,
      });
    }
    // 旧密码模式
    if (oldPassword && userEntityData.password !== oldPassword) {
      // 接口鉴权, 校验用户是否拥有权限
      await this.userService.verifyPermission(request, session.username);
      return new ResponseBody({
        message: '密码错误',
        code: ResponseCode.Fail,
      });
    }
    // 更新密码
    await this.userService.update(
      { id: userEntityData.id },
      {
        password,
      },
    );
    return new ResponseBody({
      message: '修改密码成功',
      code: ResponseCode.Success,
    });
  }

  @NoLogin()
  // 发送验证码
  @Post('code')
  async sendCode(
    @Body('email') email: string,
    @Session() session: SessionType,
  ) {
    if (email === undefined) {
      return new ResponseBody({
        message: '邮箱为空',
        code: ResponseCode.Fail,
      });
    }

    const code = randomCode(6);
    await this.emailService.sendEmail({
      to: email,
      subject: 'userCode',
      context: {
        nickname: email,
        description: 'user code verify',
        content: 'random code: ' + code,
      },
    });
    // 记录一下当前发送的验证码
    session.userCode = code;
    return new ResponseBody('发送邮件成功');
  }

  // 获取用户的基础信息包括权限树、角色列表
  @Get('login_info')
  async getUserInfo(@Session() session: SessionType) {
    const userEntity = await this.userService.findUserWithRolePermission({
      username: session.username,
    });

    if (!userEntity) {
      throw new HttpException(
        {
          message: '用户登入信息失效',
          code: ResponseCode.Fail,
          data: null,
        },
        // 401表示登入状态有误
        HttpStatus.UNAUTHORIZED,
      );
    }

    const roles = userEntity.userRoles.map((userRole) => userRole.role);
    // 使用map是为了防止有相同的 permission
    const permissionIdMap: {
      [key: number]: PermissionEntity;
    } = {};
    roles.reduce((prev, roleEntity) => {
      roleEntity.rolePermissions.reduce((prev, { permission }) => {
        permissionIdMap[permission.id] = permission;
        return prev;
      }, permissionIdMap);
      return prev;
    }, permissionIdMap);

    const permissionTree = createPermissionTreeByPath(
      Object.values(permissionIdMap),
    );

    // 先获取角色，然后获取每个角色对应的权限
    return new ResponseBody<any>({
      data: {
        ...userEntity,
        userRoles: undefined,
        roles: roles.map((role) => ({
          ...role,
          rolePermissions: undefined,
        })),
        permissionTree,
        permissionList: Object.values(permissionIdMap),
      },
      code: ResponseCode.Success,
    });
  }

  // 退出登入
  @NoLogin()
  @Delete('login_info')
  async outLogin(@Session() session: SessionType) {
    session.username = null;
    session.userId = null;
    return await new Promise((resolve, reject) => {
      session.save(function (error) {
        if (error) {
          reject(
            new HttpException(
              {
                message: error,
                code: ResponseCode.Fail,
                data: null,
              },
              HttpStatus.INTERNAL_SERVER_ERROR,
            ),
          );
          return;
        }
        session.regenerate(function (error) {
          if (error) {
            reject(
              new HttpException(
                {
                  message: error,
                  code: ResponseCode.Fail,
                  data: null,
                },
                HttpStatus.INTERNAL_SERVER_ERROR,
              ),
            );
            return;
          }
          resolve(new ResponseBody('退出登入'));
        });
      });
    });
  }

  @Get(':id')
  async getUserById(
    @Param('id') userId: number,
    @Req() request: HttpRequest,
    @Session() session: SessionType,
  ) {
    // 接口鉴权
    await this.userService.verifyPermission(request, session.username);
    const userEntity = await this.userService.findUserWithRole({
      id: userId,
    });

    const roles = userEntity.userRoles.map((userRole) => userRole.role);
    // 先获取角色，然后获取每个角色对应的权限
    return new ResponseBody<any>({
      data: {
        ...userEntity,
        userRoles: undefined,
        roles: roles.map((role) => ({
          ...role,
          rolePermissions: undefined,
        })),
      },
      code: ResponseCode.Success,
    });
  }

  // 更新用户信息
  @Put(':id')
  async updateUserBy(
    @Param('id') userId: number,
    @Body() requestBody: UserModifyRequest,
    @Req() request: HttpRequest,
    @Session() session: SessionType,
  ) {
    // 接口鉴权
    await this.userService.verifyPermission(request, session.username);
    const { roleIds, ...toUpdateUserInfo } = requestBody;
    await this.userService.updateUserWithRole(
      userId,
      toUpdateUserInfo,
      roleIds,
    );
    return new ResponseBody('更新成功');
  }
}
